Namespace ShapeLibrary
    '''
    ''' ShapeFill is a simple object that provides a few stock "fills" for
    ''' filled shapes like the ellipse.  The goal of this object
    ''' is to provide a sample of a type converter that participates
    ''' in code geneneration through the use of InstanceDescriptors.
    '''
    <TypeConverter(GetType(ShapeFill.ShapeFillConverter))> _
    Friend Class ShapeFill
        Private m_brush As Brush

        '''
        ''' A standard fill that really doesn't fill at all.
        '''
        Friend Shared ReadOnly None As ShapeFill = New ShapeFill(Nothing)

        '''
        ''' A standard fill that fills with a simple solid color.
        '''
        Friend Shared Solid As ShapeFill = New ShapeFill(Brushes.BlueViolet)

        Friend Shared somewhat As ShapeFill = New ShapeFill(Brushes.Blue)

        '''
        ''' A standard fill that fills with a gradient color.
        '''
        Friend Shared ReadOnly Gradient As ShapeFill = New ShapeFill(New System.Drawing.Drawing2D.LinearGradientBrush(New Rectangle(0, 0, 20, 20), Color.Red, Color.Blue, 45))

        '''
        ''' We want to limit our ability to provide additional fills, so we 
        ''' make the constructor private.
        '''
        Friend Sub New(ByVal b As Brush)
            m_brush = b
        End Sub

        '''
        ''' This is the brush object that shapes actually use to fill with.
        '''
        Friend ReadOnly Property Brush() As Brush
            Get
                Return m_brush
            End Get
        End Property

        ''' 
        ''' A TypeConverter for the ShapeFill object.  Note that we can make it internal, private, or
        ''' any scope we want and the designers will still be able to use it through the
        ''' TypeDescriptor object.  This type converter provides two capabilities.  First, it
        ''' provides a drop-down in the property grid for the available values that ShapeFill
        ''' can have.  Second, it provides the ability to convert to an object called
        ''' an InstanceDescriptor.  This object can be used by the code engine to 
        ''' generate source code that can create an instance of a ShapeFill object.
        ''' 
        Friend Class ShapeFillConverter
            Inherits TypeConverter

            Private m_standardValues As StandardValuesCollection
            Private m_names As String()
            Private m_values As Object()

            '''
            ''' We override this from TypeConverter.  This is called when someone wants to 
            ''' convert an object from some source type to an instance of ShapeFill, and
            ''' returns true if it can.  We override this because we want to be able
            ''' to convert from a string back to an instance, for example, "Gradient"
            ''' would return ShapeFill.Gradient.
            '''
            Public Overloads Overrides Function CanConvertFrom(ByVal context As ITypeDescriptorContext, ByVal sourceType As Type) As Boolean
                If sourceType Is GetType(String) Then
                    Return True
                End If

                ' If we don't support the conversion, we call the base class 
                ' and see if it can.
                Return MyBase.CanConvertFrom(context, sourceType)
            End Function

            ''' 
            ''' We override this from TypeConverter.  This is called when someone
            ''' wants to convert an instance of ShapeFill to another type.  We
            ''' support two types of conversions:  we can convert to a string, 
            ''' so an instance of ShapeFill.Gradient will show up as "Gradient",
            ''' and we can convert to an InstanceDescriptor object.  An 
            ''' InstanceDescriptor describes how to get an instance of the given object.
            ''' So taking Gradient as an example, an InstanceDescriptor for Gradient
            ''' would have a parameter that points to the static field "Gradient".
            ''' 
            Public Overloads Overrides Function CanConvertTo(ByVal context As ITypeDescriptorContext, ByVal destinationType As Type) As Boolean
                If destinationType Is GetType(String) Or destinationType Is GetType(InstanceDescriptor) Then
                    Return True
                End If

                ' Always call the base to see if it can perform the conversion.
                '
                Return MyBase.CanConvertTo(context, destinationType)
            End Function

            '''
            ''' This performs the actual conversion from an object to an instance of
            ''' ShapeFill.  We implement string conversion here.
            '''
            Public Overloads Overrides Function ConvertFrom(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object) As Object
                If TypeOf value Is String Then
                    FillValues()
                    Dim i As Integer
                    For i = 0 To m_names.Length - 1
                        If value.Equals(m_names(i)) Then
                            Return m_values(i)
                        End If
                    Next i
                End If

                ' And again, we call base to see if it can satisfy the conversion
                ' request.
                Return MyBase.ConvertFrom(context, culture, value)
            End Function

            '''
            ''' This performs the actual conversion from a ShapeFill to another type.
            ''' We support converting a ShapeFill to a string, or to an InstanceDescriptor.
            '''
            Public Overloads Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object, ByVal destinationType As Type) As Object
                If destinationType Is GetType(String) Then
                    FillValues()
                    Dim i As Integer
                    For i = 0 To m_values.Length - 1
                        If value Is m_values(i) Then
                            Return m_names(i)
                        End If
                    Next i

                Else
                    If destinationType Is GetType(InstanceDescriptor) Then
                        Dim fillName As String = ConvertToString(context, culture, value)
                        Dim field As FieldInfo = GetType(ShapeFill).GetField(fillName)

                        ' InstanceDescriptor needs some sort of MemberInfo from
                        ' the System.Reflection namespace as a first parameter.
                        ' This identifies the type and constructor/property/field/method
                        ' where the instance of the object is located.  The
                        ' second parameter here is an array of agruments to
                        ' pass to a method invocation or constructor.  Here we're
                        ' just accessing a field, which takes no arguments, so we
                        ' pass in null.
                        Return New InstanceDescriptor(field, Nothing)
                    End If
                End If

                ' Always call base if we can't convert.
                Return MyBase.ConvertTo(context, culture, value, destinationType)
            End Function

            '''
            ''' This is a private helper method that creates the name/value array
            ''' that we use when converting names to values.  It uses reflection
            ''' to locate all friend static fields in the ShapeFill class.
            '''
            Private Sub FillValues()
                If m_names Is Nothing Then
                    Dim type As Type = GetType(ShapeFill)
                    Dim fields As FieldInfo() = type.GetFields(BindingFlags.Static Or BindingFlags.GetField Or BindingFlags.Public)
                    ReDim m_names(fields.Length - 1)
                    ReDim m_values(fields.Length - 1)

                    Dim i As Integer

                    For i = 0 To fields.Length - 1
                        m_names(i) = fields(i).Name
                        m_values(i) = fields(i).GetValue(Nothing)
                    Next i
                End If
            End Sub

            '''
            ''' This returns a collection of "standard values" for the ShapeFill class.
            ''' This is used by the property grid to support a drop-down list of
            ''' selectable values.
            '''
            Public Overloads Overrides Function GetStandardValues(ByVal context As ITypeDescriptorContext) As StandardValuesCollection
                If m_standardValues Is Nothing Then
                    FillValues()
                    m_standardValues = New StandardValuesCollection(m_values)
                End If
                Return m_standardValues
            End Function

            '''
            ''' If this returns true, then the property grid will not allow the user
            ''' to type additional values.  We want the list of ShapeFill objects
            ''' to be the only thing allowed; we don't support the user typing
            ''' random text into the property, so we return true here.
            '''
            Public Overloads Overrides Function GetStandardValuesExclusive(ByVal context As ITypeDescriptorContext) As Boolean
                Return True
            End Function

            '''
            ''' By returning true here, the property grid will display a drop-down
            ''' arrow button to the right of the property.  When clicked, it will
            ''' fill the resulting list of values by calling GetStandardValues
            ''' above.
            '''
            Public Overloads Overrides Function GetStandardValuesSupported(ByVal context As ITypeDescriptorContext) As Boolean
                Return True
            End Function
        End Class
    End Class
End Namespace